#include <glib.h>
#include <gio/gio.h>
#include <stdio.h>
#include <stdlib.h>

#define EXAMPLE_SERVICE_NAME "CYG.API"
#define EXAMPLE_OBJECT_PATH "/Simples/Example"
#define EXAMPLE_INTERFACE_NAME "Simples.Example"

// Signal handler callback function
static void on_signal_received(GDBusConnection *connection,
                              const gchar *sender_name,
                              const gchar *object_path,
                              const gchar *interface_name,
                              const gchar *signal_name,
                              GVariant *parameters,
                              gpointer user_data)
{
    // Check if received signal is ServiceSignal
    if (g_strcmp0(signal_name, "ServiceSignal") == 0) {
        const gchar *message;
        g_variant_get(parameters, "(&s)", &message);
        g_print("Received signal: %s\n", message);
    }
}

// Async call callback function
static void async_call_callback(GObject *source_object,
                               GAsyncResult *res,
                               gpointer user_data)
{
    GDBusConnection *connection = G_DBUS_CONNECTION(source_object);
    GError *error = NULL;
    GVariant *result;
    
    // Handle different method call results based on method name
    if (g_strcmp0((const gchar*)user_data, "SayHello") == 0) {
        result = g_dbus_connection_call_finish(connection, res, &error);
        if (error) {
            g_printerr("SayHello async call failed: %s\n", error->message);
            g_error_free(error);
        } else {
            const gchar *response;
            g_variant_get(result, "(&s)", &response);
            g_print("SayHello async call result: %s\n", response);
            g_variant_unref(result);
        }
    } else if (g_strcmp0((const gchar*)user_data, "AddNumbers") == 0) {
        result = g_dbus_connection_call_finish(connection, res, &error);
        if (error) {
            g_printerr("AddNumbers async call failed: %s\n", error->message);
            g_error_free(error);
        } else {
            gint32 sum;
            g_variant_get(result, "(i)", &sum);
            g_print("AddNumbers async call result: %d\n", sum);
            g_variant_unref(result);
        }
    }
    
    g_free(user_data);
}

int main(int argc, char *argv[])
{
    GError *error = NULL;
    GDBusConnection *connection;
    GMainLoop *loop;
    guint signal_subscription_id;
    
    // Create main loop
    loop = g_main_loop_new(NULL, FALSE);
    
    // Connect to D-Bus session bus
    connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
    if (connection == NULL) {
        g_printerr("Failed to connect to D-Bus: %s\n", error->message);
        g_error_free(error);
        return -1;
    }
    
    // Subscribe to signals from the service
    signal_subscription_id = g_dbus_connection_signal_subscribe(
        connection,
        EXAMPLE_SERVICE_NAME,           // Sender name
        EXAMPLE_INTERFACE_NAME,         // Interface name
        "ServiceSignal",               // Signal name
        EXAMPLE_OBJECT_PATH,           // Object path
        NULL,                          // Arguments match rule
        G_DBUS_SIGNAL_FLAGS_NONE,
        on_signal_received,            // Callback function
        NULL,                          // User data
        NULL);                         // User data free function
    
    g_print("Client connected, signal subscription successful\n");
    
    // Wait for service to start
    g_print("Waiting for service to start...\n");
    g_usleep(1000000); // Wait 1 second
    
    // 1. Synchronous call to SayHello
    g_print("\n=== Synchronous Call Test ===\n");
    GVariant *sync_result = g_dbus_connection_call_sync(
        connection,
        EXAMPLE_SERVICE_NAME,           // Target service name
        EXAMPLE_OBJECT_PATH,            // Target object path
        EXAMPLE_INTERFACE_NAME,         // Interface name
        "SayHello",                    // Method name
        g_variant_new("(s)", "Synchronous Call"), // Parameters
        G_VARIANT_TYPE("(s)"),          // Return value type
        G_DBUS_CALL_FLAGS_NONE,
        -1,                            // Timeout (-1 means use default timeout)
        NULL,                          // Cancellable
        &error);
    
    if (error) {
        g_printerr("SayHello synchronous call failed: %s\n", error->message);
        g_error_free(error);
        error = NULL;
    } else {
        const gchar *response;
        g_variant_get(sync_result, "(&s)", &response);
        g_print("SayHello synchronous call result: %s\n", response);
        g_variant_unref(sync_result);
    }
    
    // 2. Synchronous call to AddNumbers
    sync_result = g_dbus_connection_call_sync(
        connection,
        EXAMPLE_SERVICE_NAME,
        EXAMPLE_OBJECT_PATH,
        EXAMPLE_INTERFACE_NAME,
        "AddNumbers",
        g_variant_new("(ii)", 15, 25),
        G_VARIANT_TYPE("(i)"),
        G_DBUS_CALL_FLAGS_NONE,
        -1,
        NULL,
        &error);
    
    if (error) {
        g_printerr("AddNumbers synchronous call failed: %s\n", error->message);
        g_error_free(error);
        error = NULL;
    } else {
        gint32 sum;
        g_variant_get(sync_result, "(i)", &sum);
        g_print("AddNumbers synchronous call result: 15 + 25 = %d\n", sum);
        g_variant_unref(sync_result);
    }
    
    // 3. Asynchronous call to SayHello
    g_print("\n=== Asynchronous Call Test ===\n");
    g_dbus_connection_call(
        connection,
        EXAMPLE_SERVICE_NAME,
        EXAMPLE_OBJECT_PATH,
        EXAMPLE_INTERFACE_NAME,
        "SayHello",
        g_variant_new("(s)", "Asynchronous Call"),
        G_VARIANT_TYPE("(s)"),
        G_DBUS_CALL_FLAGS_NONE,
        -1,
        NULL,
        async_call_callback,
        g_strdup("SayHello")); // Pass method name for callback identification
    
    // 4. Asynchronous call to AddNumbers
    g_dbus_connection_call(
        connection,
        EXAMPLE_SERVICE_NAME,
        EXAMPLE_OBJECT_PATH,
        EXAMPLE_INTERFACE_NAME,
        "AddNumbers",
        g_variant_new("(ii)", 10, 30),
        G_VARIANT_TYPE("(i)"),
        G_DBUS_CALL_FLAGS_NONE,
        -1,
        NULL,
        async_call_callback,
        g_strdup("AddNumbers"));
    
    // Run main loop to listen for signals and handle async callbacks
    g_print("\n=== Start listening for signals (will exit after 10 seconds) ===\n");
    
    // Set to exit after 10 seconds
    g_timeout_add_seconds(10, (GSourceFunc)g_main_loop_quit, loop);
    
    // Run main loop
    g_main_loop_run(loop);
    
    // Cleanup resources
    g_dbus_connection_signal_unsubscribe(connection, signal_subscription_id);
    g_object_unref(connection);
    g_main_loop_unref(loop);
    
    g_print("Client exiting\n");
    return 0;
}
